﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace Xceed.Silverlight.Data
{
  public sealed class ItemChangedDescription
  {
    #region Constructors

    public ItemChangedDescription( object item )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      this.Item = item;
      m_readOnlyChangedProperties = new ReadOnlyCollection<PropertyChangedDescription>( m_changedProperties );
    }

    public ItemChangedDescription( object item, string propertyName )
      : this( item, new string[] { propertyName } )
    {
    }

    public ItemChangedDescription( object item, string propertyName, object oldValue )
      : this( item, new PropertyChangedDescription[] { new PropertyChangedDescription( propertyName, oldValue ) } )
    {
    }

    public ItemChangedDescription( object item, IEnumerable<string> propertiesName )
      : this( item, ItemChangedDescription.CreateDescriptions( propertiesName ) )
    {
    }

    public ItemChangedDescription( object item, IEnumerable<PropertyChangedDescription> propertiesChanged )
      : this( item )
    {
      if( propertiesChanged == null )
      {
        propertiesChanged = new PropertyChangedDescription[ 0 ];
      }

      PropertyChangedDescription[] propertyChangedDescriptions = propertiesChanged.ToArray();

      if( propertyChangedDescriptions.Length > 0 )
      {
        if( ItemChangedDescription.HasDuplicateValues( propertyChangedDescriptions ) )
          throw new ArgumentException( "The specified property must be unique.", "propertiesChanged" );

        m_changedProperties.AddRange( propertyChangedDescriptions );
      }
    }

    #endregion

    #region Item Property

    public object Item
    {
      get;
      private set;
    }

    #endregion

    #region ChangedProperties Property

    public ICollection<PropertyChangedDescription> ChangedProperties
    {
      get
      {
        return m_readOnlyChangedProperties;
      }
    }

    private List<PropertyChangedDescription> m_changedProperties = new List<PropertyChangedDescription>( 0 );
    private ReadOnlyCollection<PropertyChangedDescription> m_readOnlyChangedProperties;

    #endregion

    internal ItemChangedDescription CreateWithNewItem( object item )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      return new ItemChangedDescription( item, m_readOnlyChangedProperties );
    }

    private static IEnumerable<PropertyChangedDescription> CreateDescriptions( IEnumerable<string> propertiesNames )
    {
      if( propertiesNames == null )
        return Enumerable.Empty<PropertyChangedDescription>();

      return propertiesNames.Select( ( propertyName ) => new PropertyChangedDescription( propertyName ) );
    }

    private static bool HasDuplicateValues( IEnumerable<PropertyChangedDescription> propertiesChanged )
    {
      if( ( propertiesChanged != null ) && ( propertiesChanged.Any() ) )
      {
        HashSet<string> distinctNames = new HashSet<string>();

        foreach( PropertyChangedDescription propertyChanged in propertiesChanged )
        {
          string propertyName = propertyChanged.PropertyName;

          if( distinctNames.Contains( propertyName ) )
            return true;

          distinctNames.Add( propertyName );
        }
      }

      return false;
    }
  }
}
